#!/usr/bin/python
#encoding=utf8
#author:linluxiang@gmail.com
#version 1.0.0
'''
这个程序的作用是进行语义分析并生成代码对象。
代码对象的结构定义在symbols.py中的CodeObject类。
CodeObject有2个部分组成，
第一个部分是name，用来保存所有Identifier以及Idntifier的值。
第二个部分是code，存放的是生成的中间代码。
中间代码是由一个Tuple来表示的，第一个是操作码，后面的都是参数。
在整个分析程序分析完毕以后，将这个代码对象存入文件，后缀名为cmmc，供虚拟机解释执行。
'''
import StringIO
import thread
import threading
import lex
import syntax
import symbols
from symbols import Node as Node
from symbols import CodeObject as Code
from symbols import Stack as Stack
import sys
import pickle

lock=threading.Lock()
userIn=StringIO.StringIO()
userOut=StringIO.StringIO()
code=Code()
file=''
userOutFlag=False
def MakeName(id,type='Integer',size=(1,)):
    temp={}
    temp['name']=id[1]
    temp['type']=type
    temp['size']=size
    temp['value']={}
    temp['value'][(0,)]=('Integer',0,0)
    temp['line']=id[2]
    return temp
#------------下面的Analyze程序是关键部分。作为生成中间代码的主要程序------
def Analyze(node):
    global code
    if isinstance(node,Node):#保证必须是Node类的实例

        #---下面开始根据node[0]来判断要进行怎样的操作----
        if node.get(0)=='Identify':
            name=MakeName(node.get(1))
            if code.hasName(name):#判断是否已经存在name了。不存在根据不同的Statement处理。
                array=0
                #以下程序判断是否有array部分，有则将array值入栈，无则将0入栈。
                if node.length()>2:
                    for x in xrange(2,node.length()):
                        Analyze(node.get(x))
                    return code.getName(name)
                else:
                    code.addCode(('PUSH_DATA',('Integer',0,0)))
                    return code.getName(name)
            else:
                return name
        #----ArrayStatement---
        elif node.get(0)=='ArrayStatement':
            Analyze(node.get(2))

        #-----WriteStatement---   
        elif node.get(0)=='WriteStatement':
            name=Analyze(node.get(3))#node的第4个位置是Identify。
            #code.addCode(('LOAD_NAME',name))
            code.addCode(('WRITE',0))


        #-----ReadStatement-------
        elif node.get(0)=='ReadStatement':
            name=Analyze(node.get(3))
            code.addCode(('READ',name))

        #-----IfStatement-----
        elif node.get(0)=='IfStatement':
            Analyze(node.get(3))
            num=code.addCode(('JUMP_IF_FALSE',0))
            Analyze(node.get(5))#Statement
            num1=code.addCode(('PASS',0))
            code.setCode(num,('JUMP_IF_FALSE',num1))
            if node.length()==8:#--有else
                Analyze(node.get(7))
                num2=code.addCode(('PASS',0))
                code.setCode(num1,('JUMP',num2))

        #------WhileStatement---------
        elif node.get(0)=='WhileStatement':
            Analyze(node.get(3))#这里是一个condition
            num=code.addCode(('JUMP_IF_FALSE',0))
            Analyze(node.get(5))
            Analyze(node.get(3))#再次进行判断
            code.addCode(('JUMP',num))#跳转到结果判断语句
            num1=code.addCode(('PASS',0))
            code.setCode(num,('JUMP_IF_FALSE',num1))


        #-----DefineStatement--------
        elif node.get(0)=='DefineStatement':
            if node.get(1)[1]=='int':#node的第2个位置是DefineWord，int或者real。
                idType='Integer'
            else:idType='Real'
            name=MakeName(node.get(2).get(1))
            #print 'name=',name
            code.addName(name)
            name=Analyze(node.get(2))
            #code.addName(name)
            code.setNameType(name,idType)
            code.addCode(('MAKE_NAME',name))
            #print node.get(3)
            if node.get(2).length()>=3:
                code.addCode(('MAKE_ARRAY',(name,node.get(2).length()-2)))
                #code.addCode(('POP_TOP',0))
            elif isinstance(node.get(3),Node):
                Analyze(node.get(4))
                code.addCode(('STORE_NAME',name))
            elif node.get(3)[1]=='=':
                Analyze(node.get(4))
                code.addCode(('STORE_NAME',name))
                #code.addCode(('POP_TOP',0))

        #------AssignStatement------
        elif node.get(0)=='AssignStatement':
            i=0
            names=[]
            for nod in node.getAll():
                if isinstance(node.get(i),Node):
                    if node.get(i).get(0)=='Identify':
                        names.append(Analyze(node.get(i)))
                        #name=Analyze(node.get(i))
                        #code.addCode(('STORE_NAME',id))
                    elif node.get(i).get(0)=='Expression':
                        Analyze(node.get(i))
                i+=1
            names=names[::-1]
            for name in names:
                code.addCode(('STORE_NAME',name))
            code.addCode(('POP_TOP',0))

        #------Condition--------
        elif node.get(0)=='Condition':
            Analyze(node.get(1))
            op=Analyze(node.get(2))
            Analyze(node.get(3))
            code.addCode(('COMPARE',op))

        #------Operator------
        elif node.get(0)=='Operator':
            return node.get(1)

        #-----Integer--------
        elif node.get(0)=='Integer':
            code.addCode(('PUSH_DATA',node.get(1)))
            #return node.get(1)

        #-----Real-----------
        elif node.get(0)=='Real':
            code.addCode(('PUSH_DATA',node.get(1)))
            #return node.get(1)

        #-------Factor----------
        elif node.get(0)=='Factor':
            if isinstance(node.get(1),Node):#判断是否是Int或者Real或者正负号
                if node.get(1).get(0)=='Operator':#正负号
                    op=Analyze(node.get(1))
                    if isinstance(node.get(2),Node):#判断是否是Integer或者Real或者Identify
                        #以下需要判断是否是Id的原因在于，在factor里面出现的Id都是需要读取
                        #它的值来进行计算用的，所以必须加上LOAD_NAME。
                        if node.get(1).get(0)=='Identify':
                            name=Analyze(node.get(2))
                            code.addCode(('LOAD_NAME',name))
                        else:
                            Analyze(node.get(2))
                    else:
                        Analyze(node.get(3))#判断是'('
                    code.addCode(('MAKE_SIGN',op))
                else:
                    if isinstance(node.get(1),Node):#判断是否是Integer或者Real或者Identify
                        if node.get(1).get(0)=='Identify':
                            name=Analyze(node.get(1))
                            code.addCode(('LOAD_NAME',name))
                        else:
                            Analyze(node.get(1))
                    else:
                        Analyze(node.get(2))#判断是'('
            else:
                Analyze(node.get(2)) 

        #-------Term------------
        elif node.get(0)=='Term':
            length=node.length()
            Analyze(node.get(1))
            for i in xrange(2,length,2):
                op=Analyze(node.get(i))
                Analyze(node.get(i+1))
                code.addCode(('CAL_DATA',op))
        #---------Expression---------
        elif node.get(0)=='Expression':
            length=node.length()
            if node.get(1).get(0)=='Operator':
                code.addCode(('PUSH_DATA',('Integer',0,0)))
                op=Analyze(node.get(1))
                Analyze(node.get(2))
                code.addCode(('CAL_DATA',op))
                start=3
            else:
                Analyze(node.get(1))
                start=2
            for i in xrange(start,length,2):
                op=Analyze(node.get(i))
                Analyze(node.get(i+1))
                code.addCode(('CAL_DATA',op))
        else:
            for n in node.getAll()[1:]:
                Analyze(n)
def ReadSourceFile(filename):
    global file
    if filename!=None:
        file=filename.split('.')[0]
        file=''.join([file,'.cmmc'])
        return open(filename).readlines()

def GetTree(lines):
    global tree,userIn,userOut,userOutFlag,lock
    syntax.userIn=userIn
    syntax.userOut=userOut

    syntax.userOutflag=userOutFlag
    tree=syntax.getTree(lines)

    return tree

def Domain():
    global file,code,userIn,userOut
    filename=raw_input('Please input your filename:')
    if __name__=='__main__':
        userIn=sys.stdin
        userOut=sys.stdout
    Compile(filename)
    

def Compile(filename):
    global file,code
    if filename!=None:
        file=filename.split('.')[0]
        file=''.join([file,'.cmmc'])
        tree=GetTree(open(filename).readlines())
        userOutFlag=syntax.userOutflag
        if isinstance(tree,Node):
            f=open(file,'w')
            Analyze(tree)
            pickle.dump(code,f)
            f.close()
            codes=code.getCodes()
#            for c in codes:
#                print c
            code=Code()
            file=''

def main():
    Domain()
if __name__=='__main__':
    main()
